package com.ygu.websocket;

import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.ygu.websocket.domain.enums.WSBaseResp;
import com.ygu.websocket.domain.enums.WSReqTypeEnum;
import com.ygu.websocket.srevice.WebSocketService;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;


/**
 * 对于netty建立连接的时候做的事情
 */
@Slf4j
@ChannelHandler.Sharable
public class NettyWebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    public WebSocketService webSocketService;


    /**
     * 第一次用户/游客 建立连接之后我们要保存用户的通道 进行消息的收发
     * @param context ChannelHandlerContext代表了一个ChannelHandler和ChannelPipeline之间的关系，
     * @throws Exception 异常
     */
    @Override
    public void channelActive(ChannelHandlerContext context) throws Exception{
        //为了后续这个类里面需要使用的时候进行直接就可以拿到这个对象 不需要进行注入 (单例模式)
        webSocketService = SpringUtil.getBean(WebSocketService.class);
        //将通道放入进行连接操作
       webSocketService.connect(context.channel());
    }

    /**
     * 客户端断开连接
     * @param context ChannelHandlerContext代表了一个ChannelHandler和ChannelPipeline之间的关系，
     */
    @Override
    public void channelInactive(ChannelHandlerContext context){
        userOffLine(context.channel());
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof WebSocketServerProtocolHandler.HandshakeComplete){
            log.info("握手成功！");
        } else if(evt instanceof IdleStateEvent){
            //用于用户心跳发送
            if(((IdleStateEvent) evt).state()== IdleState.READER_IDLE){
                log.info("当30s为给出心跳 目前Netty正在读空闲  用户主动下线");
                //当30s后读空闲 之后就是用户下线操作  可以防止内存溢出
                //TODO:用户下线 1.用户主动下线
                userOffLine(ctx.channel());
            }
        }
    }

    /**
     * 用户下线
     * @param channel 对应用户的通道
     */
    private void userOffLine(Channel channel) {
        webSocketService.userOffLine(channel);
        channel.close();
    }

    /**
     * 当客户端和服务器第一次连接的时候
     * 当NettyWebSocketServer里面的run方法 连接通信之后 会到这个方法进行操作
     * 当连接完成之后 可能前端就会发来登入的请求
     * @param channelHandlerContext 接收到客户端的消息
     * @param textWebSocketFrame 将消息转换为Text格式
     * @throws Exception 异常
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        //接收到的消息
        String text = textWebSocketFrame.text();
        log.info("接收到的消息{}",text);
        WSBaseResp wsBaseResp = JSONUtil.toBean(text, WSBaseResp.class);
        WSReqTypeEnum typeEnum = WSReqTypeEnum.of(wsBaseResp.getType());
        switch (typeEnum){
            case LOGIN:
                log.info("登入 -> 请求二维码操作");
                this.webSocketService.handleLogin(channelHandlerContext.channel());
            case AUTHORIZE:
                log.info("请求认证操作");
                break;
            case HEARTBEAT:
                log.info("请求心跳");
                break;
            default:
                log.info("未知类型");
         }
    }
}
